function varargout = GUI(varargin)
% GUI MATLAB code for GUI.fig
%      GUI, by itself, creates a new GUI or raises the existing
%      singleton*.
%
%      H = GUI returns the handle to a new GUI or the handle to
%      the existing singleton*.
%
%      GUI('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in GUI.M with the given input arguments.
%
%      GUI('Property','Value',...) creates a new GUI or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before GUI_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to GUI_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help GUI

% Last Modified by GUIDE v2.5 25-Jan-2013 00:34:24


% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @GUI_OpeningFcn, ...
                   'gui_OutputFcn',  @GUI_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT


% --- Executes just before GUI is made visible.
function GUI_OpeningFcn(hObject, eventdata, handles, varargin)
% add workpath to subdirectories
currentPath = eval('pwd');
addpath(strcat(currentPath, '\neuron'));  % add path to files in neuron directory
addpath(strcat(currentPath, '\synapse'));  % add path to files in synapse directory
addpath(strcat(currentPath, '\connectome'));  % add path to files in connectome directory

% Choose default command line output for GUI
handles.output = hObject;
% Update handles structure
guidata(hObject, handles);

% initialization of connectome object - save connectome =0 to guidata
    handles.connectome = 0;
    guidata(hObject, handles);

function axes1_CreateFcn(hObject, eventdata, handles)
function axes3_CreateFcn(hObject, eventdata, handles)
function axes4_CreateFcn(hObject, eventdata, handles)

% --- Outputs from this function are returned to the command line.
function varargout = GUI_OutputFcn(hObject, eventdata, handles) 
% Get default command line output from handles structure
varargout{1} = handles.output;

% --- Executes during object creation, after setting all properties.
function edit9_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end
function edit9_Callback(hObject, eventdata, handles)
% --- Executes during object creation, after setting all properties.
function edit5_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% --- Executes during object creation, after setting all properties.
function slider6_CreateFcn(hObject, eventdata, handles)
% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

% --- Executes during object creation, after setting all properties.
function edit14_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function edit15_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


%% BUTTON FUNCTIONS

% (...) Select file with connectome
function pushbutton7_Callback(hObject, eventdata, handles)
[filename, pathname, filterindex] = uigetfile('*.txt', 'Select file with conntome');
if(isnumeric(filename) || isnumeric(pathname))
    return;
end
label = strcat(pathname,filename);
set(handles.edit5, 'String', label);

% (Load )
function pushbutton1_Callback(hObject, eventdata, handles)
filepath = get(handles.edit5, 'String');
if(~isempty(filepath))
    [connectome, description] = loadConnectomeFromFile(filepath);
    set(handles.edit14, 'String', description);
    % save connectome as global variable
    handles.connectome = connectome;
    guidata(hObject, handles);
    %% initialization of figure
    set(handles.axes1, 'XLim', [1.8 10]);
    set(handles.axes1, 'YLim', [0 22]);
    axes(handles.axes1);
    cla; %clear current axis
    hold on;
    text( 2, 20, 'Connectome is successfully loaded!');
    string = sprintf('  input size: %u', connectome.inputSize);
    text( 2, 18, string);
    string = sprintf('  output size : %u', connectome.outputSize);
    text( 2, 16, string);
    string = sprintf('  number of layers: %u', connectome.numberOfLayers);
    text( 2, 14, string);
    string = sprintf('  neurons: %u', (length(connectome.neurons)-(connectome.inputSize + connectome.outputSize)));
    text( 2, 12, string);
    string = sprintf('  neurons in layer: %s', mat2str(connectome.neuronInLayerVector));
    text( 2, 10, string);
else
    msgbox('You didn`t select connectome file, so you cannot create it', ...,
        'Warning','warn');
end

% (Test)
function pushbutton5_Callback(hObject, eventdata, handles)
inputVectorString = get(handles.edit9, 'String');
epochsToRunString = get(handles.edit15, 'String');

DrawDeadSynapses = get(handles.checkbox2, 'Value');
MarkMemorySynapses = get(handles.checkbox4, 'Value');
% load connectome
connectome = handles.connectome;
firstMemory = 0;

if(~isempty(inputVectorString) && ~isempty(epochsToRunString))
    inputVector = str2num(inputVectorString);
    epochsToRun = str2num(epochsToRunString);
    if((isempty(inputVector)) || (isempty(epochsToRun)) || (isnan(epochsToRun)))
        msgbox('You put incorrect parameters.', ...,
        'Error','error');
        return;
    end
    if(length(inputVector) ~= connectome.inputSize)
        msgbox('You put input vector with incorrect size. It should be equal to connectome input layer size.', ...,
        'Error','error');
        return;
    end
else
    msgbox('You didn`t put parameters.', ...,
        'Error','error');
    return;
end

%connectome = randomizeSynapsesWeights(connectome);
if(isnumeric(connectome) && connectome == 0)
    msgbox('You didn`t load connectome.', ...,
        'Warning','warn');
end
%% learning algorithm


for epoch=1:epochsToRun
    connectome = learningProcess(connectome, inputVector, 1, firstMemory);
 
    for count=1:length(connectome.synapses)
        if (connectome.synapses(count).epochsWithoutUse > connectome.synapses(count).deathThreshold)
            connectome.synapses(count).active = 0;
        else
        connectome.synapses(count).active = 1;
        end
        if (connectome.synapses(count).weight > connectome.synapses(count).memoryThreshold)
            connectome.synapses(count).belongsToMemory = 1;
            if (firstMemory == 0)
                firstMemory = 1;
                if (~exist('epochOfMemory','var'))
                    epochOfMemory = epoch;
                end
            end
        else
            connectome.synapses(count).belongsToMemory = 0;
        end
    end
    percentage = (epoch*100)/epochsToRun;
    set(handles.text27, 'String', percentage);
    drawnow
end

if (~exist('epochOfMemory','var'))
    epochOfMemory = 0;
end


handles.connectome = connectome;
guidata(hObject, handles);

axes(handles.axes3);
set(handles.axes3, 'XTick', []);
set(handles.axes3, 'YTick', []);
%% draw connectome
% initialization of figure
cla; %clear current axis
width = 1000;
height = 500;
maximalSynapseWidth = 5;
hold on;
leftMargin = 5;             % left margin of whole drawing
leftConnectomeMargin = 100; % left margin of Connectome (before 
                            % connectome there will be a input layer)
leftOutputMargin = 25;      % space between last layer of connectome and output
                            % layer
topMargin = 5;
sizeOfRectangle = (get(handles.slider6, 'Value'))/2; %half of the square line which is symbol for neuron
VerticalDistanceBetweenNeuronInLayer = ((height - 2*topMargin) / connectome.maximalNumberOfNeuronsInLayer);
HorizontalDistanceBetweenNeuronInLayer = ((width - 2*leftMargin) / (connectome.numberOfLayers+2));


% draw synapses
[minSynapseWeight, maxSynapseWeight] = getMinimalAndMaximalSynapseWeight(connectome);
for i=1:length(connectome.synapses)
    synapse = connectome.synapses(i);

    Active = synapse.active;
    Weight = synapse.weight;                 % synapse weight
    InId = synapse.inputNeuronId;
    InLay = synapse.inputNeuronLayer;
    OutId = synapse.outputNeuronId;
    OutLay = synapse.outputNeuronLayer;
    InMemory = synapse.belongsToMemory;
    MemoryThreshold = synapse.memoryThreshold;
    DeathThreshold = synapse.deathThreshold;

    if(Active == 1 || (get(handles.checkbox2, 'Value') == 0))
        %% find start of synapse
        if(InLay == 0)
            numberOfNeuronsInCurrentLayer = connectome.inputSize;

            if(mod(numberOfNeuronsInCurrentLayer,2) == 1)
                StartPositionX = (leftConnectomeMargin - leftMargin)/2;
                StartPositionY = (height -(2*topMargin))/2 + (InId-(numberOfNeuronsInCurrentLayer+1)/2)*VerticalDistanceBetweenNeuronInLayer;
            else
                StartPositionX = (leftConnectomeMargin - leftMargin)/2;
                StartPositionY = (height -(2*topMargin))/2 -0.5*VerticalDistanceBetweenNeuronInLayer + (InId -(numberOfNeuronsInCurrentLayer)/2)*VerticalDistanceBetweenNeuronInLayer;
            end
        elseif(InLay >= 1 && InLay <= connectome.numberOfLayers)
            numberOfNeuronsInCurrentLayer = connectome.neuronInLayerVector(InLay);
            if(mod(numberOfNeuronsInCurrentLayer,2) == 1)
                StartPositionX = leftMargin + leftConnectomeMargin + (InLay-1)*HorizontalDistanceBetweenNeuronInLayer;
                StartPositionY = (height -(2*topMargin))/2 + (InId-(numberOfNeuronsInCurrentLayer+1)/2)*VerticalDistanceBetweenNeuronInLayer;
            else
                StartPositionX = leftMargin + leftConnectomeMargin + (InLay-1)*HorizontalDistanceBetweenNeuronInLayer;
                StartPositionY = (height -(2*topMargin))/2 -0.5*VerticalDistanceBetweenNeuronInLayer + (InId -(numberOfNeuronsInCurrentLayer)/2)*VerticalDistanceBetweenNeuronInLayer;
            end
        else
            numberOfNeuronsInCurrentLayer = connectome.outputSize;
            if(mod(numberOfNeuronsInCurrentLayer,2) == 1)
                StartPositionX = leftConnectomeMargin + leftMargin + HorizontalDistanceBetweenNeuronInLayer * connectome.numberOfLayers + leftOutputMargin;
                StartPositionY = (height -(2*topMargin))/2 + (InId-(numberOfNeuronsInCurrentLayer+1)/2)*VerticalDistanceBetweenNeuronInLayer;
            else
                StartPositionX = leftConnectomeMargin + leftMargin + HorizontalDistanceBetweenNeuronInLayer * connectome.numberOfLayers + leftOutputMargin;
                StartPositionY = (height -(2*topMargin))/2 -0.5*VerticalDistanceBetweenNeuronInLayer + (InId -(numberOfNeuronsInCurrentLayer)/2)*VerticalDistanceBetweenNeuronInLayer;
            end
        end

        %% find end of synapse
        if(OutLay == 0)
            numberOfNeuronsInCurrentLayer = connectome.inputSize;

            if(mod(numberOfNeuronsInCurrentLayer,2) == 1)
                EndPositionX = (leftConnectomeMargin - leftMargin)/2;
                EndPositionY = (height -(2*topMargin))/2 + (OutId-(numberOfNeuronsInCurrentLayer+1)/2)*VerticalDistanceBetweenNeuronInLayer;
            else
                EndPositionX = (leftConnectomeMargin - leftMargin)/2;
                EndPositionY = (height -(2*topMargin))/2 -0.5*VerticalDistanceBetweenNeuronInLayer + (OutId -(numberOfNeuronsInCurrentLayer)/2)*VerticalDistanceBetweenNeuronInLayer;
            end
        elseif(OutLay >= 1 && OutLay <= connectome.numberOfLayers)
            numberOfNeuronsInCurrentLayer = connectome.neuronInLayerVector(OutLay);
            if(mod(numberOfNeuronsInCurrentLayer,2) == 1)
                EndPositionX = leftMargin + leftConnectomeMargin + (OutLay-1)*HorizontalDistanceBetweenNeuronInLayer;
                EndPositionY = (height -(2*topMargin))/2 + (OutId-(numberOfNeuronsInCurrentLayer+1)/2)*VerticalDistanceBetweenNeuronInLayer;
            else
                EndPositionX = leftMargin + leftConnectomeMargin + (OutLay-1)*HorizontalDistanceBetweenNeuronInLayer;
                EndPositionY = (height -(2*topMargin))/2 -0.5*VerticalDistanceBetweenNeuronInLayer + (OutId -(numberOfNeuronsInCurrentLayer)/2)*VerticalDistanceBetweenNeuronInLayer;
            end
        else
            numberOfNeuronsInCurrentLayer = connectome.outputSize;
            if(mod(numberOfNeuronsInCurrentLayer,2) == 1)
                EndPositionX = leftConnectomeMargin + leftMargin + HorizontalDistanceBetweenNeuronInLayer * connectome.numberOfLayers + leftOutputMargin;
                EndPositionY = (height -(2*topMargin))/2 + (OutId-(numberOfNeuronsInCurrentLayer+1)/2)*VerticalDistanceBetweenNeuronInLayer;
            else
                EndPositionX = leftConnectomeMargin + leftMargin + HorizontalDistanceBetweenNeuronInLayer * connectome.numberOfLayers + leftOutputMargin;
                EndPositionY = (height -(2*topMargin))/2 -0.5*VerticalDistanceBetweenNeuronInLayer + (OutId -(numberOfNeuronsInCurrentLayer)/2)*VerticalDistanceBetweenNeuronInLayer;
            end
        end
        % draw synapse
        synapseLineWidth = (Weight - minSynapseWeight + 1)/(maxSynapseWeight - minSynapseWeight +1)*maximalSynapseWidth; % +1 is applied to make some line for synapses with minimal value
        colorMemorySynapse = get(handles.checkbox4, 'Value');
        if((InMemory == 1) && (colorMemorySynapse == 1))
        patch([StartPositionX, StartPositionX+2*synapseLineWidth, EndPositionX+2*synapseLineWidth, EndPositionX],...,
                        [StartPositionY, StartPositionY+2*synapseLineWidth, EndPositionY+2*synapseLineWidth, EndPositionY],'r', 'LineWidth', 1);
        else
            patch([StartPositionX, StartPositionX+synapseLineWidth, EndPositionX+synapseLineWidth, EndPositionX],...,
                        [StartPositionY, StartPositionY+synapseLineWidth, EndPositionY+synapseLineWidth, EndPositionY],'k', 'LineWidth', 1);
        end
    end
end

%% Draw connectome
for i=1:length(connectome.neurons)
    neuron = connectome.neurons(i);

    neuronLayer = neuron.layer;
    numberInLayer = neuron.numberInLayer;

    if(neuronLayer == 0)
        numberOfNeuronsInCurrentLayer = connectome.inputSize;

        if(mod(numberOfNeuronsInCurrentLayer,2) == 1)
            XPosition = (leftConnectomeMargin - leftMargin)/2;
            YPosition = (height -(2*topMargin))/2 + (numberInLayer-(numberOfNeuronsInCurrentLayer+1)/2)*VerticalDistanceBetweenNeuronInLayer;
            patch([XPosition-sizeOfRectangle, XPosition-sizeOfRectangle, XPosition+sizeOfRectangle, XPosition+sizeOfRectangle],...,
                    [YPosition-sizeOfRectangle, YPosition+sizeOfRectangle, YPosition+sizeOfRectangle, YPosition-sizeOfRectangle],'b');
        else
            XPosition = (leftConnectomeMargin - leftMargin)/2;
            YPosition = (height -(2*topMargin))/2 -0.5*VerticalDistanceBetweenNeuronInLayer + (numberInLayer -(numberOfNeuronsInCurrentLayer)/2)*VerticalDistanceBetweenNeuronInLayer;
            patch([XPosition-sizeOfRectangle, XPosition-sizeOfRectangle, XPosition+sizeOfRectangle, XPosition+sizeOfRectangle],...,
                    [YPosition-sizeOfRectangle, YPosition+sizeOfRectangle, YPosition+sizeOfRectangle, YPosition-sizeOfRectangle],'b');
        end
    elseif (neuronLayer >= 1 && neuronLayer <= connectome.numberOfLayers)
        numberOfNeuronsInCurrentLayer = connectome.neuronInLayerVector(neuronLayer);

        if(mod(numberOfNeuronsInCurrentLayer,2) == 1)
            XPosition = leftMargin + leftConnectomeMargin + (neuronLayer-1)*HorizontalDistanceBetweenNeuronInLayer;
            YPosition = (height -(2*topMargin))/2 + (numberInLayer-(numberOfNeuronsInCurrentLayer+1)/2)*VerticalDistanceBetweenNeuronInLayer;
            patch([XPosition-sizeOfRectangle, XPosition-sizeOfRectangle, XPosition+sizeOfRectangle, XPosition+sizeOfRectangle],...,
                    [YPosition-sizeOfRectangle, YPosition+sizeOfRectangle, YPosition+sizeOfRectangle, YPosition-sizeOfRectangle],'g');
        else
            XPosition = leftMargin + leftConnectomeMargin + (neuronLayer-1)*HorizontalDistanceBetweenNeuronInLayer;
            YPosition = (height -(2*topMargin))/2 -0.5*VerticalDistanceBetweenNeuronInLayer + (numberInLayer -(numberOfNeuronsInCurrentLayer)/2)*VerticalDistanceBetweenNeuronInLayer;
            patch([XPosition-sizeOfRectangle, XPosition-sizeOfRectangle, XPosition+sizeOfRectangle, XPosition+sizeOfRectangle],...,
                    [YPosition-sizeOfRectangle, YPosition+sizeOfRectangle, YPosition+sizeOfRectangle, YPosition-sizeOfRectangle],'r');
        end
    else

        numberOfNeuronsInCurrentLayer = connectome.outputSize;

        if(mod(numberOfNeuronsInCurrentLayer,2) == 1)
            XPosition = leftConnectomeMargin + leftMargin + HorizontalDistanceBetweenNeuronInLayer * connectome.numberOfLayers + leftOutputMargin;
            YPosition = (height -(2*topMargin))/2 + (numberInLayer-(numberOfNeuronsInCurrentLayer+1)/2)*VerticalDistanceBetweenNeuronInLayer;
            patch([XPosition-sizeOfRectangle, XPosition-sizeOfRectangle, XPosition+sizeOfRectangle, XPosition+sizeOfRectangle],...,
                    [YPosition-sizeOfRectangle, YPosition+sizeOfRectangle, YPosition+sizeOfRectangle, YPosition-sizeOfRectangle],'y');
        else
            XPosition = leftConnectomeMargin + leftMargin + HorizontalDistanceBetweenNeuronInLayer * connectome.numberOfLayers + leftOutputMargin;
            YPosition = (height -(2*topMargin))/2 -0.5*VerticalDistanceBetweenNeuronInLayer + (numberInLayer -(numberOfNeuronsInCurrentLayer)/2)*VerticalDistanceBetweenNeuronInLayer;
            patch([XPosition-sizeOfRectangle, XPosition-sizeOfRectangle, XPosition+sizeOfRectangle, XPosition+sizeOfRectangle],...,
                    [YPosition-sizeOfRectangle, YPosition+sizeOfRectangle, YPosition+sizeOfRectangle, YPosition-sizeOfRectangle],'y');
        end
        drawnow;
    end
end

%% end draw connectome

%% draw Learning log
    NumberOfEpochsUntillFirstMemoryBranch = 0;
    axes(handles.axes4);
    set(handles.axes4, 'XLim', [1.8 10]);
    set(handles.axes4, 'YLim', [6 22]);
    set(handles.axes4, 'XTick', []);
    set(handles.axes4, 'YTick', []);
    [activeSynapses, deadSynapses, SynapsesInMemory ] = GetStatisticsOfConnectome(connectome);
    %% initialization of figure
    cla; %clear current axis
    hold on;
    text( 2, 20, 'Connectome is successfully learnt!');
    string = sprintf('  number of active synapses: %d', activeSynapses);
    text( 2, 18, string);
    string = sprintf('  number of dead synapses : %d', deadSynapses);
    text( 2, 16, string);
    string = sprintf('  number of synapse in memory: %d', SynapsesInMemory);
    text( 2, 14, string);
    
    
    string = sprintf('  number of epochs until first memory\n  branch: %d', epochOfMemory);
    text( 2, 11, string);


%% end draw learning log

% (Save as..)
function pushbutton10_Callback(hObject, eventdata, handles)

connectome = handles.connectome;
if(isnumeric(connectome) && connectome == 0)
    msgbox('You didn`t load connectome.', ...,
        'Warning','warn');
else
    [filename,pathname] = uiputfile('*.txt','Save connectome as');
    if(isnumeric(filename) || isnumeric(pathname))
        return;
    end 
    path = strcat(pathname,filename);
    if(~isempty(get(handles.edit14, 'String')))
        connectomeName = get(handles.edit14, 'String');
    else
        connectomeName = 'connectome';
    end
    saveConnectomeToFile( connectome, connectomeName, path )
end   
